#include <Arduino.h>
#include "EEPROM.h"
/*Энергонезависимая память будим хранить флаги*/
#include <string.h>
#include "dumpmon.h"

//const int rear_dor_Pin = A0; //Блютуз
//const int rear_dor_Pin = A1; //Блютуз
//const int rear_dor_Pin = A2; //Телефон
//const int rear_dor_Pin = A3; //Телефон
//const int rear_dor_Pin = A4; //Комп
//const int rear_dor_Pin = A5; //аккумулятор
//                             //Комп включен?
//                             //и 8 пинов закрытие открытие дверей

//Исходящие пины
const int open_dors_Pin = 4;
const int close_dors_Pin = 5;

const int left_signal_Pin = 6; //правый поворотник возможно стоит повесить на шим
const int right_signal_Pin = 7; //левый поворотник

const int siren_Pin = 8; //Сирена

//Входящие пины
const int signal_dors_Pin = 3; // багажник неуверен что у меня есть такой пин
//const int front_right_dor_Pin = 4; // Дверь 1
//const int front_left_dor_Pin = 5; // Дверь 1
//const int rear_right_dor_Pin = 6; // Дверь 1
//const int rear_left_dor_Pin = 7; // Дверь 1
//
//const int front_right_window_Pin = 8; // Стекло 1
//const int front_left_window_Pin = 9; // Стекло 1
//const int rear_right_window_Pin = 10; // Стекло 1
//const int rear_left_window_Pin = 11; // Стекло 1

const int warningPin = 12;
const int alertPin = 13;

//Коды ошибок
static const String ER_command_not_found = "err,command_not_found;"; //Пока пусть длинные потом сокращу до цифр
static const String ER_too_long = "err,command_long;";
static const String ER_val_not_found = "err,value_not_found;";

const boolean debug_flag = true; //флаг дебага

char command[10]; //Команда полученная с порта
int command_i = 0; //смещение в коменде, команда получается несколькими проходами одной процедуры
int command_var; //значение полученое с порта
boolean command_flag = true; //если правда то идет получение команды

boolean key_entered = false; //используется в функции шифрования будит означать что ключ введен
const int enginPin = 0; //Pin 2 прерывание
volatile boolean lastEnginStatus = false; // Переменная для прерывания сохраняет последнее состояние двигателя

void write_raw_data(String com) //XXX Может имеет смысл включить в CommandKey иначе он не полиморфен?
{
	Serial.println(com);
}

class CommandKey{ //Абстрактный класс
protected:
	String command;
	boolean varible;

public:
	CommandKey()
	{
		command = 0;
		varible = 0;
	};

	CommandKey(String com)
	{
		command = com;
		varible = false;
	}
	virtual ~CommandKey(){}; // виртуальный пустой деструктор
 	String getKey(){ return command; };
//	void setKey( String *com ){ command = *com; }
	boolean getValue(){ return varible; };
	void sayKeyValue()
	{
	write_raw_data(command + "," + varible + ";");
	}
	virtual void setValue(boolean)
	{
		sayKeyValue();
	};
};


class CommandKey_save : public CommandKey {
protected:
	int eeprom;
public:
	CommandKey_save(String com, int eep)
	{
		command = com;
		eeprom = eep;
		varible = map(EEPROM.read(eeprom), 0, 255, 0, 1);
	}

	void setValue(boolean val)
	{
		if (val != varible)
		{
		varible = val;
		if ( eeprom != -1 ) EEPROM.write(eeprom, map(val,0,1,0,255));
		}
		sayKeyValue();
	}
};

 //Класс не сохраняющий значения
class CommandKey_not_save : public CommandKey {
protected:
public:
	CommandKey_not_save(String com): CommandKey(com) {}; //TODO проверить возможно нужный конструктор вызоветься без этого указания
	CommandKey_not_save(String com, boolean val)
	{
		command = com;
		varible = val;
	}
	void setValue(boolean val)
	{
		varible = val;
		sayKeyValue();
	}
};

//TODO Класс выдающий ОК для проверки связи

//TODO Классы запросов скорее всего одно действие один класс

//Поворотники включить выключить пока что оба сразу
class CommandSignalLamp : public CommandKey {
protected:
	void on_signal(){
		digitalWrite(left_signal_Pin, true);
		digitalWrite(right_signal_Pin, true);
		varible = true;
	}
	void off_signal(){
		digitalWrite(left_signal_Pin, false);
		digitalWrite(right_signal_Pin, false);
		varible = false;
	}
public:

	CommandSignalLamp(String com)
	{
		command = com;
		pinMode(left_signal_Pin, OUTPUT);
		pinMode(right_signal_Pin, OUTPUT);
		off_signal(); //На всякий случай переводим в выключенное состояние
	}

	void setValue(boolean val)
	{
		if (val) { //XXX возможно нужны статические флаги т.к. повоортник может использовать другой класс
			on_signal();
		} else {
			off_signal();
		}
		sayKeyValue();
	}
};

//Сирену включить выключить
class CommandSiren : public CommandSignalLamp {
protected:
	void on_siren(){ //XXX Пока что ХЗ как ей пользоваться может быть это шим
		digitalWrite(siren_Pin, true);
		varible = true;
	}
	void off_siren(){
		digitalWrite(siren_Pin, false);
		varible = false;
	}
public:
	CommandSiren(String com): CommandSignalLamp(com)
	{
		pinMode(siren_Pin, OUTPUT);
		off_siren();
	};

	void setValue(boolean val)
	{
		if (val) { //TODO Надо что то придумать для мигания поворотниками
			on_siren();
			on_signal();
		} else {
			off_siren();
			off_signal();
		}
		sayKeyValue();
	}
};

//Двери открыть закрыть
class CommandDor : public CommandSiren {
protected:
	void pic() //XXX вот тут ковыряться уже на машине со звуком и светом
	{
		on_siren();
		on_signal();
		delay(500);
		off_signal();
		delay(500);
		off_siren();
	}
	void open_dor()
	{
		digitalWrite(close_dors_Pin, false);
		digitalWrite(open_dors_Pin, true);
		pic();
		digitalWrite(open_dors_Pin, false);
		varible = true;
	}
	void close_dor()
	{
		digitalWrite(open_dors_Pin, false);
		digitalWrite(close_dors_Pin, true);
		pic();
		digitalWrite(close_dors_Pin, false);
		pic();
		varible = false;
	}
public:

	CommandDor(String com) : CommandSiren(com)
	{
		command = com;
		pinMode(open_dors_Pin, OUTPUT);
		pinMode(close_dors_Pin, OUTPUT);
		close_dor(); //При включении закрываем двери
	}

	void setValue(boolean val)
	{
		if (val) { //TODO Очень глупо надо добавить логики в последствии
			open_dor();
		} else {
			close_dor();
		}
		sayKeyValue();
	}
};





class AllKeys{
private:

	static const int command_key_size = 6;
	CommandKey *keys[command_key_size];


	int getKeyIndex(String *com)
	{
		for (int i = 0; i < command_key_size; i++) {
			if ( keys[i]->getKey() == *com ){
				return i;
			}
		}
		write_raw_data(ER_command_not_found);
		return -1; //Надо писать исключение но кроскомпилятор не поддерживает
	}
public:
	AllKeys(){};

	void init()
	{
		keys[0] = new CommandKey_save("st_w", 1); //Стресс сенсор ворнинг
		keys[1] = new CommandKey_save("st_a", 2); //Стресс сенсор флерт
		keys[2] = new CommandKey_save("st_g", 3); //Стресс сенсор стекло
		keys[3] = new CommandDor("dor_open"); //Открывание дверей
		keys[4] = new CommandSiren("siren_on"); //Включение сирены
		keys[5] = new CommandSignalLamp("signal_on"); //Включение поворотников

//		keys.initKey("f_r_dor", 4); //двери
//		keys.initKey("f_l_dor", 5);
//		keys.initKey("r_r_dor", 6);
//		keys.initKey("r_l_dor", 7);
//		keys.initKey("r_dor", 8); //багажник
	}

	int setKey(String *com, boolean var) //TODO вместо возврата вызывать сразу write_raw_data
	{
		//Нельзя написать класс выдающий все ключи придется сделать десь затычку
		if (*com == "all_flags") {
			getAllFlags();
			return 0;
		}
		int i = getKeyIndex(com);
		if (i != -1 )
		{
			keys[i]->setValue(var);
			return 0;
		}
		else
		{
			return -1;
		}
	}
//	int getValue( String *com) // Придется возвращать инт вместо исключения т.к. не поддерживается
//	{
//		int i = getKeyIndex(com);
//		if (i != -1 )
//			{
//			return (keys[i]->getValue()); // надеюсь автоматическое преобразование типов работает
//			}
//		else
//		{
//			return -1;
//		}
//	}
	void getKey( String *com) // Придется возвращать инт вместо исключения т.к. не поддерживается
	{
		int i = getKeyIndex(com);
		if (i != -1 )
			{
			(keys[i]->sayKeyValue());
			}
		else
		{
			write_raw_data(ER_command_not_found);
		}
	}
	void getAllFlags() //XXX Не нужная функция будим запрашивать ключи по одному оставляю для отладки
	{
		for (int i = 0; i < command_key_size ; i++) {
			keys[i]->sayKeyValue();
			}
	}
};

AllKeys flags; // Объявление здесь потому что классы и тело программы в одном файле

void read_raw_data()
{
	String command_str;
	char buffer;
	if ( command_i > 10 )
	{ //Команда не может быть длинной больее 10 символов
		command_i = 0;
		command_flag = true;
		Serial.flush(); //Сбрасываем нафиг буфер там какая то муть
		write_raw_data(ER_too_long);
		return;
	}
	buffer = Serial.read();
	if ( buffer == ',' ) {
		command_flag=false;
		command[command_i++] = '\0'; //Нужно что бы отчистить предыдущую команду
		return; //запятая не нужна
	}
	if ( buffer == ';' ) { //возможно не нужная функция
		command_flag=true;
		command_i = 0; //Новая команда началась старую прибеваем
		return; //точка с запятой не нужна
	}
	if ( buffer == '\n' ) {
		command_flag=true;
		command_i = 0; //Новая команда началась старую прибеваем
		return; //энтер не нужен
	}
	if ( command_flag == true ) {
		command[command_i++] = buffer;
	} else {
		command_var = int(buffer);
		command_str = command; //преобразование в String очень тупое
			if ( command_var == 49 )
			{	flags.setKey(&command_str, true);
			} else if (command_var == 48) {
				flags.setKey(&command_str, false);
			} else if (command_var == 63) {
				flags.getKey(&command_str); //запрос значения ключа
			}else{
				write_raw_data(ER_val_not_found);
			}
//		flags.setKey(&command, command_var);
//		parse_message(&command_str, command_var);
		command_flag = true;
		command_i = 0;
	}
}

		void setup()
		{
			Serial.begin(19200);
//////////////////////////////////дебаг//////////////////////////////////
//			dumpmonSetup(19200);              // Call DumpMon setup function

//			  watch(&text, "text");             // Add text string watch
//			  watch(&PORTB, "PORTB");           // Add register watch
/////////////////////////////////////////////////////////////////////////
			pinMode(warningPin, INPUT);
			pinMode(alertPin, INPUT);
			//произойдет фигня если включить ардуино при работающем двигателе поэтому флаг надо как то определеть здесь напрямую
			lastEnginStatus = digitalRead(2); //Вот так вот напрямую указываем пин к которому подключен провод зажигания потому что он обрабатывается прерыванием
//			pinMode(open_dors_Pin, OUTPUT);
//			pinMode(close_dors_Pin, OUTPUT);
//			CommandDor temp = CommandDor("dor_open");
//			temp.setValue(0);
//			delay(1000);
//			temp.setValue(1);
			Serial.println("Started");
			flags.init(); //инициализируем объект здесь так как pinMode нельзя выполнить за пределами setup|loop
		}

		void enginStarted()
		{
			if (lastEnginStatus != true)
			{
				//Здесь то что должно выполняться при запуске двигателя
				//Проверки оборудования
				//Запуск компа
				lastEnginStatus = true; //И завершающее сброс флага
			}
//если есть данные - читаем
			if (Serial.available())
			{
				//процедура обработки данных
				read_raw_data();
			}
		}

		void loop()
		{

			//если есть данные - читаем
			if (Serial.available())
			{
				//процедура обработки данных
				read_raw_data();
			}

			if (lastEnginStatus != false)
			{
				//Здесь то что должно выполняться при выключении двигателя
				//Проверки оборудования
				//выключение компа
				//запуск сигналки
				//закрытие стекол
				lastEnginStatus = false; //И завершающее сброс флага
			}

			//может быть придется здесь добавить процедуру антидрожания контакта поэтому собираем инфу тут
//			boolean lastAlert = digitalRead(alertPin);
//			boolean lastWarning = digitalRead(warningPin);
//			boolean last_front_right_dor = digitalRead(front_right_dor_Pin);
//			boolean last_front_left_dor = digitalRead(front_left_dor_Pin);
//			boolean last_rear_right_dor = digitalRead(rear_right_dor_Pin);
//			boolean last_rear_left_dor = digitalRead(rear_left_dor_Pin);
//			boolean last_rear_dor = digitalRead(rear_dor_Pin);

//			//Алерт
//			if (lastAlert == HIGH && lastWarning == HIGH
//					&& keys.getValue("st_a") == 1)
//			{
//				//Бля какие то гандоны ударили машину
//				Serial.println("stress_alert");
//				//врубаем тревогу
//				//Звоним на мобилу
//				//включаем камеру
//			}
//			//Разбилось стекло
//			else if (lastAlert == HIGH && lastWarning == LOW
//					&& keys.getValue("st_g") == 1)
//			{
//				//Сложный с интерпретации сигнал с микрофона может стекло выбили а может салют
//				Serial.println("stress_warning");
//				//Звоним на мобилу
//				//включаем камеру
//			}
//			//Внимание
//			else if (lastAlert == HIGH && lastWarning == LOW
//					&& keys.getValue("st_w") == 1)
//			{
//				Serial.println("stress_glass");
//				//легкий удар включить камеру
//			}
//
//			if (last_front_right_dor == HIGH
//					&& keys.getValue("f_r_dor") == 1)
//			{
//				//сработал концевик двери кто то вломился
//				Serial.println("front_right_dor");
//				//врубаем тревогу
//				//Звоним на мобилу
//				//включаем камеру
//			}

		}
